import {MockConfigItem} from '../entity/mock-config-item';
import {deepCopy, EntityUtils} from './entity-utils';
import {DocService} from '../service/doc.service';
import {EntityRef} from '../entity/entity-ref';
import {Entity} from '../entity/entity';
import {NumberMock} from './mock/number-mock';
import {StringMock} from './mock/string-mock';
import {BooleanMock} from './mock/boolean-mock';
import {ByteMock} from './mock/byte-mock';
import {DateMock} from './mock/date-mock';
import {IMock} from './mock/i-mock';
import {CollectionMock} from './mock/collection-mock';
import {BehaviorSubject} from 'rxjs';
import {AlertMessage} from '../entity/alert-message';
import {MockConditionItem} from '../entity/mock-condition-item';
import {EnumMock} from './mock/enum-mock';
import format from 'date-fns/format';
import {CharMock} from './mock/char-mock';
import {MapMock} from './mock/map-mock';

export class MockUtils {
    private static mockInfoBehavior = new BehaviorSubject<AlertMessage>(null);

    static mockInfoProvider = MockUtils.mockInfoBehavior.asObservable();

    private static MOCK_TYPE_MAP = {
        0: new NumberMock(),
        1: new StringMock(),
        2: new BooleanMock(),
        3: new ByteMock(),
        4: new DateMock(),
        5: new EnumMock(),
        6: new CharMock(),

        100: new CollectionMock(),
        101: new MapMock(),
    };

    private static BASE_TYPE_MAP = {
        'number': 0,
        'java.lang.Long': 0,
        'long': 0,
        'Long': 0,
        'java.lang.Integer': 0,
        'int': 0,
        'Int': 0,
        'java.lang.Double': 0,
        'double': 0,
        'Double': 0,
        'java.lang.Float': 0,
        'float': 0,
        'Float': 0,
        'java.math.BigDecimal': 0,
        'BigDecimal': 0,

        'java.lang.String': 1,
        'string': 1,
        'String': 1,

        'java.lang.Boolean': 2,
        'boolean': 2,
        'Boolean': 2,

        'java.lang.Byte': 3,
        'byte': 3,

        'java.util.Date': 4,
        'Date': 4,

        // enum: 5

        'char': 6,
        'Character': 6,
        'java.lang.Character': 6,
    };

    public static setMockInfo(message: AlertMessage) {
        MockUtils.mockInfoBehavior.next(message);
        MockUtils.mockInfoBehavior.next(null);
    }

    public static getBaseTypeMockConfigDisplay(mockConfigItem: MockConfigItem): string {
        const mock: IMock = MockUtils.MOCK_TYPE_MAP[mockConfigItem.baseType];
        if (mock) {
            return mock.getBaseTypeMockConfigDisplay(mockConfigItem);
        }
        return mockConfigItem.value;
    }

    /**
     * 生成某个EntityRef的Mock配置
     * @param fieldRef
     * @param docService
     * @param loadedEntityMap
     */
    public static getMockConfig(fieldRef: EntityRef, docService: DocService, loadedEntityMap: Map<string, MockConfigItem>): MockConfigItem {
        const entity = docService.getEntity(fieldRef.entityName);
        const config = new MockConfigItem();
        if (fieldRef.name) {
            config.key = fieldRef.name;
        }
        config.entityName = fieldRef.entityName;
        config.mockValueRandomList = [''];

        config.type = EntityUtils.getEntityType(entity);
        switch (config.type) {
            case 0:
                // 基本类型
                this.setConfigItemValue(config, entity, fieldRef);
                break;
            case 1:
                // 枚举
                config.value = fieldRef.demo;
                config.baseType = 5;
                config.mockValueRandomList = [];
                entity.fields.forEach(field => config.mockValueRandomList.push(field.name));
                break;
            case 2:
                // pojo
                if (entity.fields) {
                    config.children = [];
                    if (!loadedEntityMap.has(entity.name)) {
                        loadedEntityMap.set(entity.name, config);
                        entity.fields.forEach(field => {
                            const fieldMockConfig = MockUtils.getMockConfig(field, docService, loadedEntityMap);
                            config.children.push(fieldMockConfig);
                        });
                    } else {
                        config.loaded = true;
                    }
                }
                break;
            case 3:
                // collection
                config.mockValueStart = 1;
                if (!entity.parameteredEntityRefs || entity.parameteredEntityRefs.length === 0) {
                    // 没有指定泛型！
                    break;
                }
                const listItemEntityRef = entity.parameteredEntityRefs[0];
                const listItemEntity = docService.getEntity(listItemEntityRef.entityName);

                const mockItemConfig = this.getMockConfig(listItemEntityRef, docService, loadedEntityMap);
                if (EntityUtils.isPrimitive(listItemEntity) && fieldRef.demo) {
                    // 如果是基本类型
                    let listData;

                    try {
                        listData = JSON.parse(fieldRef.demo);
                    } catch (e) {

                    }
                    if (!listData || listData.length === 0) {
                        config.children = [mockItemConfig];
                    } else {
                        config.children = [];
                        listData.forEach(item => {
                            const itemConfig = deepCopy(mockItemConfig);
                            itemConfig.value = item;
                            config.children.push(itemConfig);
                        });
                    }
                } else {
                    config.children = [mockItemConfig];
                }
                break;
            case 4:
                // Map
                if (entity.parameteredEntityRefs && entity.parameteredEntityRefs.length === 2) {
                    const paramType = entity.parameteredEntityRefs[1];
                    const paramItem: MockConfigItem = MockUtils.getMockConfig(paramType, docService, loadedEntityMap);
                    paramItem.key = 'hello';
                    config.children = [paramItem];
                    config.mapValueConfig = paramItem;
                }
                break;
            default:
                // 5=Array
                break;
        }

        return config;
    }

    private static setConfigItemValue(config, entity, fieldRef: EntityRef) {
        config.baseType = MockUtils.getBaseType(entity);
        // 基础类型：0=number 1=string 2=boolean 3=byte 4=date 5=enum 6=char
        switch (config.baseType) {
            case 0:
                if (!isNaN(+fieldRef.demo)) {
                    config.value = fieldRef.demo;
                } else if (typeof (fieldRef.demo) !== 'undefined' && fieldRef.demo !== null) {
                    // demo 值不是数值！
                    this.fieldRefDemoNotMatch(fieldRef, entity);
                }
                break;
            case 2:
                if (typeof (fieldRef.demo) === 'undefined') {
                    break;
                }
                if (fieldRef.demo.match(/[tru|fals]e/i)) {
                    config.value = fieldRef.demo;
                } else {
                    MockUtils.setMockInfo(AlertMessage.error(fieldRef.name + '字段的类型' + entity.name + '与demo值:' + fieldRef.demo + '，不匹配！'));
                }
                break;
            case 4:
                if (!config.value) {
                    config.value = format(new Date(), 'yyyy-MM-dd HH:mm:ss');
                }
                break;
            case 5:
                break;
            case 6:
                break;
            default:
                config.value = fieldRef.demo;
                break;
        }
        if (!config.value) {
            config.value = EntityUtils.getBaseTypeDefaultValue(entity.name);
        }
    }

    private static fieldRefDemoNotMatch(fieldRef: EntityRef, entity) {
        MockUtils.setMockInfo(AlertMessage.error(fieldRef.name + '字段的类型' + entity.name + '与demo值:' + fieldRef.demo + '，不匹配！'));
    }

    public static getBaseType(entity: Entity): number {
        const entityName = entity.name;
        const type = MockUtils.BASE_TYPE_MAP[entityName];
        if (!type) {
            if (entity.enumerate) {
                return 5;
            }
        }
        if (type == null) {
            console.error(type + '无法定位类型：' + entityName);
        }
        return type;
    }

    public static getCollectionTypeMockConfigDisplay(mockConfigItem: MockConfigItem) {
        return MockUtils.MOCK_TYPE_MAP[100].getBaseTypeMockConfigDisplay(mockConfigItem);
    }

    static checkValue(mockConfigItem: MockConfigItem, mapKey?: string) {
        let mock: IMock = null;
        if (mockConfigItem.type === 3 || mockConfigItem.type === 5) {
            // collection
            mock = MockUtils.MOCK_TYPE_MAP[100];
        } else if (mockConfigItem.type === 4) {
            // map
            const mapMock: MapMock = MockUtils.MOCK_TYPE_MAP[101];
            mapMock.mapKey = mapKey;
            mock = mapMock;
        } else {
            mock = MockUtils.MOCK_TYPE_MAP[mockConfigItem.baseType];
        }
        if (mock) {
            return mock.onSave(mockConfigItem);
        }

        return true;
    }

    static emptyMockConditionItem() {
        return <MockConditionItem>{
            key: '',
            value: '',
            operator: '==',
            available: false
        };
    }

    static checkNumber(number: number, min: number, max: number, title: string): boolean {
        if (!number) {
            MockUtils.setMockInfo(AlertMessage.error(title + '不能为空！'));
            return false;
        }
        const num = number;
        if (num !== Number.MIN_VALUE && num <= min) {
            MockUtils.setMockInfo(AlertMessage.error(title + '必须大于' + min + '！'));
            return false;
        }
        if (max && num > max) {
            MockUtils.setMockInfo(AlertMessage.error(title + '必须小于等于' + max + '！'));
            return false;
        }
        return true;
    }
}
